//
// -----------------------------------------------------------------------------
// (c) Bob Jenkins, March 1996, Public Domain
// You may use this code in any way you wish, and it is free.  No warrantee.
// -----------------------------------------------------------------------------
//

#include "rand.h"

// external results
ub4 randrsl[256];
ub4 randcnt;

// internal state
static ub4 mm[256];
static ub4 aa=0, bb=0, cc=0;

void isaac()
{
	register ub4 i,x,y;

	cc = cc + 1;	// cc just gets incremented once per 256 results
	bb = bb + cc;	// then combined with bb

	for (i=0; i<256; ++i) {
		x = mm[i];
		switch (i%4) {
			case 0: aa = aa^(aa<<13); break;
			case 1: aa = aa^(aa>>6); break;
			case 2: aa = aa^(aa<<2); break;
			case 3: aa = aa^(aa>>16); break;
		}
		aa			= 		mm[(i+128)%256] + aa;
		mm[i]		= y	 = 	mm[(x>>2)%256] + aa + bb;
		randrsl[i]	= bb = 	mm[(y>>10)%256] + x;

		// Note that bits 2..9 are chosen from x but 10..17 are chosen
		// from y.  The only important thing here is that 2..9 and 10..17
		// don't overlap.  2..9 and 10..17 were then chosen for speed in
		// the optimized version (rand.c)
		// See http://burtleburtle.net/bob/rand/isaac.html
		// for further explanations and analysis.
	}
}


// if (flag!=0), then use the contents of randrsl[] to initialize mm[].
#define mix(a,b,c,d,e,f,g,h) \
{ \
	a^=b<<11; d+=a; b+=c; \
	b^=c>>2;  e+=b; c+=d; \
	c^=d<<8;  f+=c; d+=e; \
	d^=e>>16; g+=d; e+=f; \
	e^=f<<10; h+=e; f+=g; \
	f^=g>>4;  a+=f; g+=h; \
	g^=h<<8;  b+=g; h+=a; \
	h^=a>>9;  c+=h; a+=b; \
}

void randinit(int flag)
{
	int i;
	ub4 a,b,c,d,e,f,g,h;
	aa=bb=cc=0;
	a=b=c=d=e=f=g=h=0x9e3779b9;  // the golden ratio

	// scramble it
	for (i=0; i<4; ++i)	{
		mix(a,b,c,d,e,f,g,h);
	}

	for (i=0; i<256; i+=8) { // fill in mm[] with messy stuff
		if (flag) {	// use all the information in the seed
			a+=randrsl[i  ]; b+=randrsl[i+1]; c+=randrsl[i+2]; d+=randrsl[i+3];
			e+=randrsl[i+4]; f+=randrsl[i+5]; g+=randrsl[i+6]; h+=randrsl[i+7];
		}
		mix(a,b,c,d,e,f,g,h);
		mm[i  ]=a; mm[i+1]=b; mm[i+2]=c; mm[i+3]=d;
		mm[i+4]=e; mm[i+5]=f; mm[i+6]=g; mm[i+7]=h;
	}

	if (flag) { // do a second pass to make all of the seed affect all of mm
		for (i=0; i<256; i+=8) {
			a+=mm[i  ]; b+=mm[i+1]; c+=mm[i+2]; d+=mm[i+3];
			e+=mm[i+4]; f+=mm[i+5]; g+=mm[i+6]; h+=mm[i+7];
			mix(a,b,c,d,e,f,g,h);
			mm[i  ]=a; mm[i+1]=b; mm[i+2]=c; mm[i+3]=d;
			mm[i+4]=e; mm[i+5]=f; mm[i+6]=g; mm[i+7]=h;
		}
	}

	isaac();		// fill in the first set of results */
	randcnt = 256;	// prepare to use the first set of results */
}


// Get a random 64-bit number
ull GetRand64()
{
	static int i=256, cnt=0;
	if (i>254) {
		randinit(cnt++);
		isaac();
		i=0;
	}
	return (((ull) randrsl[i++] << 32ULL) | ((ull) randrsl[i++]));
}


// for testing uncomment this and you shall see some random stuff on screen
/*
int main()
{
	ub4 i,j;
	aa=bb=cc=(ub4)0;
	for (i=0; i<256; ++i) mm[i]=randrsl[i]=(ub4)0;
	randinit(1);
	for (i=0; i<2; ++i) {
		isaac();
		for (j=0; j<256; ++j) {
			printf("%.8lx",randrsl[j]);
			if ((j&7)==7) printf("\n");
		}
	}
}
*/
